﻿/*************************************************************************
 *
 * Copyright (c) 2009-2015 Xuld. All rights reserved.
 * 
 * Project Url: http://work.xuld.net/circus
 * 
 * This source code is part of the Project Circus.
 * 
 * This code is licensed under The Circus License.
 * See the file License.html for the license details.
 * 
 * 
 * You must not remove this notice, or any other, from this software.
 *
 * 
 *************************************************************************/


using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using System.Web;
using System.Web.Razor;
using System.Web.Razor.Parser.SyntaxTree;

namespace Circus.Utility.Razor {

    /// <summary>
    /// 表示一个 Razor 模板解析器。
    /// </summary>
    public static class RazorEngine {

        #region 代码生成

        /// <summary>
        /// 获取或设置内部用于解析 Razor 模板的 RazorTemplateEngine 的对象。
        /// </summary>
        public static RazorTemplateEngine razorTemplateEngine = new RazorTemplateEngine(new System.Web.Razor.RazorEngineHost(new CSharpRazorCodeLanguage()) {
            GeneratedClassContext = new System.Web.Razor.Generator.GeneratedClassContext(
                System.Web.Razor.Generator.GeneratedClassContext.DefaultExecuteMethodName,
                System.Web.Razor.Generator.GeneratedClassContext.DefaultWriteMethodName,
                System.Web.Razor.Generator.GeneratedClassContext.DefaultWriteLiteralMethodName,
                "WriteTo", "WriteLiteralTo", "System.Action<System.IO.TextWriter>", "DefineSection") {
                    ResolveUrlMethodName = "ResolveUrl"
                },
            DefaultBaseClass = "Circus.Razor.CompiledTemplate"
        });

        /// <summary>
        /// 获取当前默认使用的全局名字空间列表。
        /// </summary>
        public static ISet<string> namespaceImports {
            get {
                return razorTemplateEngine.Host.NamespaceImports;
            }
        }

        static RazorEngine() {
            namespaceImports.Add("System");
            namespaceImports.Add("System.Collections.Generic");
            namespaceImports.Add("System.Linq");
            namespaceImports.Add("Microsoft.CSharp");


            foreach (string assembly in AppDomain.CurrentDomain.GetAssemblies().TakeWhile(a => !a.IsDynamic).Select(a => a.Location).Concat(new string[1] { typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly.Location }).Distinct()) {
                compilerParameters.ReferencedAssemblies.Add(assembly);
            }

            AppDomain.CurrentDomain.AssemblyLoad += (object sender, AssemblyLoadEventArgs args) => {
                if (!args.LoadedAssembly.IsDynamic) {
                    compilerParameters.ReferencedAssemblies.Add(args.LoadedAssembly.Location);
                }
            };

        }

        /// <summary>
        /// 创建指定路径的模板解析后的模板类型。
        /// </summary>
        /// <param name="templateReader">用于获取模板内容的 <see cref="TextReader"/> 对象。</param>
        /// <param name="className">模板编译后的类名。默认为根据 <paramref name="sourceFileName"/> 动态生成。</param>
        /// <param name="rootNamespace">模板编译后的名字空间。默认为 RazorCompiledTemplates 。</param>
        /// <param name="sourceFileName">模板的文件名或模板的别名，用于在模板互相引用时使用。模板的名字作为模板的唯一标识符，应该保证其唯一性。默认为模板文件的路径或模板字符串本身的哈希值。</param>
        /// <returns>一个 <see cref="GeneratorResults"/> 对象。</returns>
        public static GeneratorResults generateCode(TextReader templateReader, string className, string rootNamespace, string sourceFileName) {
            return razorTemplateEngine.GenerateCode(templateReader, className, rootNamespace, sourceFileName);
        }

        #endregion

        #region 代码编译

        /// <summary>
        /// 获取或设置内部用于编译代码的 CodeDomProvider 对象。
        /// </summary>
        public static CodeDomProvider codeDomProvider = new CSharpCodeProvider();

        /// <summary>
        /// 获取或设置编译 Razor 文件使用的参数。
        /// </summary>
        public static CompilerParameters compilerParameters = new CompilerParameters {
            GenerateInMemory = true,
            GenerateExecutable = false,
            IncludeDebugInformation = false,
            CompilerOptions = "/target:library /optimize"
        };

        /// <summary>
        /// 获取当前项目所引用的程序集。
        /// </summary>
        /// <value>一个集合，包含由源引用以进行编译的程序集名称。</value>
        public static System.Collections.Specialized.StringCollection referencedAssemblies {
            get {
                return compilerParameters.ReferencedAssemblies;
            }
        }

        /// <summary>
        /// 存储已编译的模板类型。
        /// </summary>
        private static readonly Dictionary<string, object> _compiledTemplates = new Dictionary<string, object>();

        /// <summary>
        /// 编译指定路径的模板解析后的模板类型。
        /// </summary>
        /// <param name="templateReader">用于获取模板内容的 <see cref="TextReader"/> 对象。</param>
        /// <param name="sourceFileName">模板的文件名或模板的别名，用于在模板互相引用时使用。模板的名字作为模板的唯一标识符，应该保证其唯一性。默认为模板文件的路径或模板字符串本身的哈希值。</param>
        /// <returns>返回编译后的模板对象实例。</returns>
        public static object compile(TextReader templateReader, string sourceFileName) {
            object compiledTemplate;
            if (_compiledTemplates.TryGetValue(sourceFileName, out compiledTemplate)) {
                return compiledTemplate;
            }

            const string rootNamespace = "RazorCompiledTemplates";
            string className = System.Web.Razor.Parser.ParserHelpers.SanitizeClassName(Path.GetFileName(sourceFileName));

            // 1. 编译为模板源码。
            var generatorResult = generateCode(templateReader, className, rootNamespace, sourceFileName);
            if (!generatorResult.Success) {
                throw new RazorParseException(generatorResult.ParserErrors);
            }

            // 2. 编译为动态的类型。
            CompilerResults compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, generatorResult.GeneratedCode);

#if PARSER_DEBUG
            StringBuilder sb = new StringBuilder();
            TextWriter writer = new StringWriter(sb);
            CodeDomProvider.GenerateCodeFromCompileUnit(generatorResult.GeneratedCode, writer, new CodeGeneratorOptions());
            Console.Write(sb.ToString());
          File.WriteAllText("D:\\aa.cs", sb.ToString());
#endif

            if (compilerResults.Errors.Count > 0) {
                throw new RazorComplieException(compilerResults.Errors);
            }

            // 3. 获取刚创建的类型。
            Type type = compilerResults.CompiledAssembly.GetType(String.Concat(rootNamespace, ".", className));

            // 4. 加入缓存列表。
            return _compiledTemplates[sourceFileName] = Activator.CreateInstance(type);

        }

        #endregion

        #region 执行

        /// <summary>
        /// 编译指定的模板类，并使用指定的模型和上下文执行模板。
        /// </summary>
        /// <param name="templateReader">要解析的模板输入。</param>
        /// <param name="sourceFileName">模板文件路径。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <param name="outputWriter">输出器。</param>
        public static void parse(TextReader templateReader, string sourceFileName, object model, TextWriter outputWriter) {

            // 获取对应的模板类型。
            ICompiledTemplate compiledTemplate = (ICompiledTemplate)compile(templateReader, sourceFileName);

            // 编译并执行模板。
            compiledTemplate.execute(model, outputWriter);

        }

        /// <summary>
        /// 解析指定的模板内容。
        /// </summary>
        /// <param name="template">模板内容。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <returns>解析后返回的字符串。</returns>
        public static string parseString(string template, object model = null) {
            return parseString(template, model, template.GetHashCode().ToString());
        }

        /// <summary>
        /// 解析指定的模板内容。
        /// </summary>
        /// <param name="template">模板内容。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <param name="sourceFileName">模板的文件名或模板的别名，用于在模板互相引用时使用。模板的名字作为模板的唯一标识符，应该保证其唯一性。默认为模板文件的路径或模板字符串本身的哈希值。</param>
        /// <returns>解析后返回的字符串。</returns>
        public static string parseString(string template, object model, string sourceFileName) {
            StringBuilder sb = new StringBuilder();
            StringReader reader = new StringReader(template);
            StringWriter writer = new StringWriter(sb);
            parse(reader, sourceFileName, model, writer);
            return sb.ToString();
        }

        /// <summary>
        /// 解析指定的模板文件。使用 UTF-8 编码读写文件。
        /// </summary>
        /// <param name="templateFileName">模板文件路径。</param>
        /// <param name="encoding">模板文件的编码。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <param name="outputWriter">输出器。</param>
        /// <returns>返回解析后的内容。</returns>
        public static void parseFile(string templateFileName, Encoding encoding, object model, TextWriter outputWriter) {
            templateFileName = Path.GetFullPath(templateFileName);
            using (StreamReader reader = new StreamReader(templateFileName, encoding)) {
                parse(reader, templateFileName, model, outputWriter);
            }
        }

        /// <summary>
        /// 解析指定的模板文件。
        /// </summary>
        /// <param name="templateFileName">模板文件路径。</param>
        /// <param name="encoding">模板文件的编码。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <param name="saveFilePath">解析后保存的路径。</param>
        /// <param name="saveEncoding">保存的路径。</param>
        public static void parseFile(string templateFileName, Encoding encoding, object model, string saveFilePath, Encoding saveEncoding) {
            using (StreamWriter writer = new StreamWriter(saveFilePath, false, saveEncoding))
                parseFile(templateFileName, encoding, model, writer);
        }

        /// <summary>
        /// 解析指定的模板文件。使用 UTF-8 编码读写文件。
        /// </summary>
        /// <param name="templateFileName">模板文件路径。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <param name="saveFilePath">解析后保存的路径。</param>
        public static void parseFile(string templateFileName, object model, string saveFilePath) {
            parseFile(templateFileName, Encoding.UTF8, model, saveFilePath, Encoding.UTF8);
        }

        /// <summary>
        /// 解析指定的模板文件。
        /// </summary>
        /// <param name="templateFileName">模板文件路径。</param>
        /// <param name="encoding">模板文件的编码。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <returns>返回解析后的内容。</returns>
        public static string parseFile(string templateFileName, Encoding encoding, object model = null) {
            StringBuilder sb = new StringBuilder();
            parseFile(templateFileName, encoding, model, new StringWriter(sb));
            return sb.ToString();
        }

        /// <summary>
        /// 解析指定的模板文件。使用 UTF-8 编码读写文件。
        /// </summary>
        /// <param name="templateFileName">模板文件路径。</param>
        /// <param name="model">解析时使用的模型对象。</param>
        /// <returns>返回解析后的内容。</returns>
        public static string parseFile(string templateFileName, object model = null) {
            return parseFile(templateFileName, Encoding.UTF8, model);
        }

        #endregion

    }

    #region 模板依赖类

    /// <summary>
    /// 表示默认的模板对象应该实现的接口。
    /// </summary>
    public interface ICompiledTemplate {

        /// <summary>
        /// 执行当前的模板。
        /// </summary>
        /// <param name="model">执行当前模板使用的模块对象。</param>
        /// <param name="output">执行当前模板的输出器。</param>
        void execute(object model, TextWriter output);

    }

    /// <summary>
    /// 表示编译后的模板默认基类。
    /// </summary>
    /// <typeparam name="TModel">Model 属性的类型。</typeparam>
    public abstract class CompiledTemplateBase<TModel> : ICompiledTemplate {

        /// <summary>
        /// 获取或设置当前的模型对象。
        /// </summary>
        public TModel Model {
            get;
            set;
        }

        private dynamic _viewBag;

        /// <summary>
        /// 获取当前的自定义存储对象。
        /// </summary>
        public dynamic ViewBag {
            get {
                if (_viewBag == null) {
                    _viewBag = new ExpandoObject();
                }
                return _viewBag;
            }
        }

        /// <summary>
        /// 获取页面当前的 <see cref="T:System.IO.TextWriter" /> 对象。
        /// </summary>
        /// <returns>
        /// <see cref="T:System.IO.TextWriter" /> 对象。</returns>
        public TextWriter Output {
            get;
            set;
        }

        /// <summary>
        /// 执行当前的模板。
        /// </summary>
        public abstract void Execute();

        /// <summary>
        /// 执行当前的模板。
        /// </summary>
        /// <param name="model">执行当前模板使用的模块对象。</param>
        /// <param name="output">执行当前模板的输出器。</param>
        void ICompiledTemplate.execute(object model, TextWriter output) {
            Model = (TModel)model;
            Output = output;
            Execute();
        }

        /// <summary>
        /// 模板的文件名或模板的别名，用于在模板互相引用时使用。
        /// </summary>
        public string SourceFileName {
            get;
            set;
        }

        static readonly HtmlHelper _htmlHelper = new HtmlHelper();

        /// <summary>
        /// 获取与页关联的 <see cref="T:System.Web.WebPages.Html.HtmlHelper" /> 对象。
        /// </summary>
        /// <returns>可以在页面中呈现 HTML 窗体控件的对象。</returns>
        public HtmlHelper Html {
            get {
                return _htmlHelper;
            }
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入。
        /// </summary>
        /// <param name="value">要编码并写入的对象。</param>
        public void Write(object value) {
            if (value != null) {
                WriteTo(Output, value.ToString());
            }
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入。
        /// </summary>
        /// <param name="value">要编码并写入的对象。</param>
        public void Write(Action<TextWriter> value) {
            if (value != null) {
                value(Output);
            }
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入。
        /// </summary>
        /// <param name="value">要编码并写入的对象。</param>
        public void Write(HtmlString value) {
            if (value != null) {
                WriteLiteralTo(Output, value.ToString());
            }
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入。
        /// </summary>
        /// <param name="value">要编码并写入的对象。</param>
        public void Write(string value) {
            WriteTo(Output, System.Web.HttpUtility.HtmlEncode(value));
        }

        /// <summary>
        /// 无需先对指定的对象进行 HTML 编码，即可将其写入。
        /// </summary>
        /// <param name="value">要写入的对象。</param>
        public void WriteLiteral(object value) {
            if (value != null) {
                WriteLiteralTo(Output, value);
            }
        }

        /// <summary>
        /// 无需先对指定的对象进行 HTML 编码，即可将其写入。
        /// </summary>
        /// <param name="value">要写入的对象。</param>
        public void WriteLiteral(string value) {
            WriteLiteralTo(Output, value);
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入指定的文本编写器。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="content">要编码并写入的对象。</param>
        public static void WriteTo(TextWriter writer, object content) {
            writer.Write(content);
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入指定的文本编写器。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="content">要编码并写入的对象。</param>
        public static void WriteTo(TextWriter writer, Action<TextWriter> content) {
            content(writer);
        }

        /// <summary>
        /// 将指定的对象作为 HTML 编码的字符串写入指定的文本编写器。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="content">要编码并写入的对象。</param>
        public static void WriteTo(TextWriter writer, string content) {
            writer.Write(content);
        }

        /// <summary>
        /// 无需 HTML 编码即可将指定的对象写入指定的 <see cref="T:System.IO.TextWriter" /> 实例。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="content">要写入的对象。</param>
        public static void WriteLiteralTo(TextWriter writer, object content) {
            writer.Write(content);
        }

        /// <summary>
        /// 无需 HTML 编码即可将指定的对象写入指定的 <see cref="T:System.IO.TextWriter" /> 实例。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="content">要写入的对象。</param>
        public static void WriteLiteralTo(TextWriter writer, string content) {
            writer.Write(content);
        }

        /// <summary>
        /// 将指定的对象作为 HTML 属性写入指定的文本编写器。
        /// </summary>
        /// <param name="name">属性名。</param>
        /// <param name="prefix">引号。</param>
        /// <param name="suffix">引号。</param>
        /// <param name="values">属性值。</param>
        public void WriteAttribute(string name, Tuple<string, int> prefix, Tuple<string, int> suffix, params AttributeValue[] values) {
            WriteAttributeTo(Output, name, prefix, suffix, values);
        }

        /// <summary>
        /// 将指定的对象作为 HTML 属性写入指定的写入指定的 <see cref="T:System.IO.TextWriter" /> 实例。
        /// </summary>
        /// <param name="writer">文本编写器。</param>
        /// <param name="name">属性名。</param>
        /// <param name="prefix">引号。</param>
        /// <param name="suffix">引号。</param>
        /// <param name="values">属性值。</param>
        public static void WriteAttributeTo(TextWriter writer, string name, Tuple<string, int> prefix, Tuple<string, int> suffix, params AttributeValue[] values) {
            bool first = true;
            bool wroteSomething = false;
            if (values.Length == 0) {
                WriteLiteralTo(writer, prefix.Item1);
                WriteLiteralTo(writer, suffix.Item1);
            } else {
                for (int i = 0; i < values.Length; i++) {
                    AttributeValue attrVal = values[i];
                    Tuple<object, int> val = attrVal.Value;

                    bool? boolVal = null;
                    if (val.Item1 is bool) {
                        boolVal = (bool)val.Item1;
                    }

                    if (val.Item1 != null && (boolVal == null || boolVal.Value)) {
                        string valStr = val.Item1 as string;
                        if (valStr == null) {
                            valStr = val.Item1.ToString();
                        }
                        if (boolVal != null) {
                            valStr = name;
                        }

                        if (first) {
                            WriteLiteralTo(writer, prefix.Item1);
                            first = false;
                        } else {
                            WriteLiteralTo(writer, attrVal.Prefix.Item1);
                        }

                        if (attrVal.Literal) {
                            WriteLiteralTo(writer, valStr);
                        } else {
                            WriteTo(writer, valStr); // Write value
                        }
                        wroteSomething = true;
                    }
                }
                if (wroteSomething) {
                    WriteLiteralTo(writer, suffix.Item1);
                }
            }
        }

        private Dictionary<string, Action> _sections;

        /// <summary>
        /// 由内容页调用以创建指定的内容部分。
        /// </summary>
        /// <param name="name">要创建的部分的名称。</param>
        /// <param name="action">在新部分中要执行的操作的类型。</param>
        public void DefineSection(string name, Action action) {
            if (_sections == null) {
                _sections = new Dictionary<string, Action>();
            }
            _sections[name] = action;
        }

        /// <summary>
        /// 在布局页中，将呈现指定部分的内容并指定该部分是否为必需。
        /// </summary>
        /// <returns>要呈现的 HTML 内容。</returns>
        /// <param name="name">要呈现的部分。</param>
        /// <param name="required">要指定该部分为必需，则为 true；否则为 false。</param>
        /// <exception cref="T:System.Web.HttpException">
        /// <paramref name="name" /> 部分已呈现。- 或 -<paramref name="name" /> 部分已标记为必需，但却找不到。</exception>
        public string RenderSection(string name, bool required = true) {
            Action writer;
            if (_sections != null && _sections.TryGetValue(name, out writer)) {
                writer();
            } else if (required) {
                throw new System.Web.HttpException(string.Format("Section not defined: \"{0}\"", name));
            }
            return null;
        }

        /// <summary>
        /// 从指定的路径返回规范化路径。
        /// </summary>
        /// <returns>规范化路径。</returns>
        /// <param name="path">要规范化的路径。</param>
        public string NormalizePath(string path) {
            return PathHelper.combine(VirtualPath, path);
        }

        /// <summary>
        /// 使用指定的参数，从应用程序相对 URL 构建绝对 URL。
        /// </summary>
        /// <returns>绝对 URL。</returns>
        /// <param name="path">要在 URL 中使用的初始路径。</param>
        /// <param name="pathParts">附加路径信息，例如文件夹和子文件夹。</param>
        public string Href(string path, params object[] pathParts) {
            return UriHelper.buildUrl(NormalizePath(path), pathParts);
        }

        /// <summary>
        /// 获取页的虚拟路径。
        /// </summary>
        /// <returns>虚拟路径。</returns>
        public string VirtualPath {
            get {
                return String.Empty;
            }
        }

        /// <summary>
        /// 呈现内容页。
        /// </summary>
        /// <returns>一个可以写入页的输出的对象。</returns>
        /// <param name="path">要呈现的页的路径。</param>
        /// <param name="model">要传递给页的数据。</param>
        public string RenderPage(string path, object model = null) {
            var newSourceFilePath = PathHelper.resolve(SourceFileName, path);
            RazorEngine.parseFile(newSourceFilePath, Output.Encoding, model, Output);
            return null;
        }

        /// <summary>
        /// 将一个相对的路径地址改成基于网站虚拟路径的绝对地址。
        /// </summary>
        /// <param name="relativeUrl"></param>
        /// <returns></returns>
        public string ResolveUrl(string relativeUrl) {
            return PathHelper.resolve(VirtualPath, relativeUrl);
        }

        /// <summary>
        /// 创建当前模板的副本。
        /// </summary>
        /// <returns></returns>
        public CompiledTemplateBase<TModel> clone() {
            return (CompiledTemplateBase<TModel>)MemberwiseClone();
        }

    }

    /// <summary>
    /// 表示基于动态模型的模板。
    /// </summary>
    public abstract class CompiledTemplate : CompiledTemplateBase<dynamic>, ICompiledTemplate {

        class RazorDynamicObject : System.Dynamic.DynamicObject {

            readonly object _model;

            public RazorDynamicObject(object model) {
                _model = model;
            }

            /// <summary>
            /// Gets the value of the specified member.
            /// </summary>
            /// <param name="binder">The current binder.</param>
            /// <param name="result">The member result.</param>
            /// <returns>True.</returns>
            [System.Diagnostics.DebuggerStepThrough]
            public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result) {
                if (binder == null)
                    throw new ArgumentNullException("binder");

                var dynamicObject = _model as RazorDynamicObject;
                if (dynamicObject != null)
                    return dynamicObject.TryGetMember(binder, out result);

                Type modelType = _model.GetType();
                var prop = modelType.GetProperty(binder.Name);
                if (prop == null) {
                    result = null;
                    return false;
                }

                object value = prop.GetValue(_model, null);
                if (value == null) {
                    result = value;
                    return true;
                }

                Type valueType = value.GetType();
                result = (TypeHelper.IsAnonymousType(valueType))
                             ? new RazorDynamicObject(value)
                             : value;
                return true;
            }

        }

        /// <summary>
        /// 执行当前的模板。
        /// </summary>
        /// <param name="model">执行当前模板使用的模块对象。</param>
        /// <param name="output">执行当前模板的输出器。</param>
        void ICompiledTemplate.execute(object model, TextWriter output) {
            Model = new RazorDynamicObject(model);
            Output = output;
            Execute();
        }

    }

    /// <summary>
    /// 为在网页中呈现 HTML 窗体控件和执行窗体验证提供支持。
    /// </summary>
    public sealed class HtmlHelper {

        /// <summary>通过使用最小编码，返回表示指定对象的 HTML 编码的字符串，该最小编码仅适用于由引号引起来的 HTML 特性。</summary>
        /// <returns>表示该对象的 HTML 编码的字符串。</returns>
        /// <param name="value">要编码的对象。</param>
        public string AttributeEncode(object value) {
            return AttributeEncode(Convert.ToString(value, CultureInfo.InvariantCulture));
        }

        /// <summary>通过使用最小编码，返回表示指定字符串的 HTML 编码的字符串，该最小编码仅适用于由引号引起来的 HTML 特性。</summary>
        /// <returns>表示原始字符串的 HTML 编码的字符串。</returns>
        /// <param name="value">要编码的字符串。</param>
        public string AttributeEncode(string value) {
            if (string.IsNullOrEmpty(value)) {
                return string.Empty;
            }
            return HttpUtility.HtmlAttributeEncode(value);
        }

        /// <summary>通过使用适用于任意 HTML 的完整编码返回一个表示指定对象的 HTML 编码的字符串。</summary>
        /// <returns>表示该对象的 HTML 编码的字符串。</returns>
        /// <param name="value">要编码的对象。</param>
        public string Encode(object value) {
            return this.Encode(Convert.ToString(value, CultureInfo.InvariantCulture));
        }

        /// <summary>通过使用适用于任意 HTML 的完整编码返回一个表示指定字符串的 HTML 编码的字符串。</summary>
        /// <returns>表示原始字符串的 HTML 编码的字符串。</returns>
        /// <param name="value">要编码的字符串。</param>
        public string Encode(string value) {
            if (string.IsNullOrEmpty(value)) {
                return string.Empty;
            }
            return HttpUtility.HtmlEncode(value);
        }

        /// <summary>包装 <see cref="T:System.Web.HtmlString" /> 实例中的 HTML 标记，以便将其解释为 HTML 标记。</summary>
        /// <returns>未编码的 HTML。</returns>
        /// <param name="value">要呈现其 HTML 的对象。</param>
        public Action<TextWriter> Raw(object value) {
            return (writer) => {
                writer.Write(value);
            };
        }

        /// <summary>包装 <see cref="T:System.Web.HtmlString" /> 实例中的 HTML 标记，以便将其解释为 HTML 标记。</summary>
        /// <returns>未编码的 HTML。</returns>
        /// <param name="value">要解释为 HTML 标记而不是进行 HTML 编码的字符串。</param>
        public HtmlString Raw(string value) {
            return new HtmlString(value);
        }

    }

    /// <summary>
    /// 表示一个已编码的 HTML 字符串。
    /// </summary>
    public sealed class HtmlString {
        private string _htmlString;

        /// <summary>
        /// 初始化。
        /// </summary>
        /// <param name="value"></param>
        public HtmlString(string value) {
            _htmlString = value;
        }

        /// <summary>
        /// 返回表示当前 <see cref="T:System.Object" /> 的 <see cref="T:System.String" />。
        /// </summary>
        /// <returns>
        ///   <see cref="T:System.String" />，表示当前的 <see cref="T:System.Object" />。
        /// </returns>
        public override string ToString() {
            return _htmlString;
        }
    }

    /// <summary>
    /// 表示一个已编码的属性值。
    /// </summary>
    public sealed class AttributeValue {

        /// <summary>
        /// 构造函数。
        /// </summary>
        /// <param name="prefix"></param>
        /// <param name="value"></param>
        /// <param name="literal"></param>
        public AttributeValue(Tuple<string, int> prefix, Tuple<object, int> value, bool literal) {
            this.Prefix = prefix;
            this.Value = value;
            this.Literal = literal;
        }

        /// <summary>
        /// 创建属性对象。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static AttributeValue FromTuple(Tuple<Tuple<string, int>, Tuple<object, int>, bool> value) {
            return new AttributeValue(value.Item1, value.Item2, value.Item3);
        }

        /// <summary>
        /// 创建属性对象。
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static AttributeValue FromTuple(Tuple<Tuple<string, int>, Tuple<string, int>, bool> value) {
            return new AttributeValue(value.Item1, new Tuple<object, int>(value.Item2.Item1, value.Item2.Item2), value.Item3);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator AttributeValue(Tuple<Tuple<string, int>, Tuple<object, int>, bool> value) {
            return FromTuple(value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static implicit operator AttributeValue(Tuple<Tuple<string, int>, Tuple<string, int>, bool> value) {
            return FromTuple(value);
        }

        /// <summary>
        /// 是否是常量。
        /// </summary>
        public bool Literal {
            get;
            private set;
        }

        /// <summary>
        /// 前缀。
        /// </summary>
        public Tuple<string, int> Prefix {
            get;
            private set;
        }

        /// <summary>
        /// 值。
        /// </summary>
        public Tuple<object, int> Value {
            get;
            private set;
        }
    }

    /// <summary>
    /// 提供类型处理相关的辅助函数。
    /// </summary>
    static class TypeHelper {

        private static readonly Type DynamicType = typeof(DynamicObject);
        private static readonly Type ExpandoType = typeof(ExpandoObject);
        private static readonly Type EnumerableType = typeof(IEnumerable);
        private static readonly Type EnumeratorType = typeof(IEnumerator);

        /// <summary>
        /// Determines if the specified type is an anonymous type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>True if the type is an anonymous type, otherwise false.</returns>
        public static bool IsAnonymousType(Type type) {
            if (type == null)
                throw new ArgumentNullException("type");

            return (type.IsClass
                    && type.IsSealed
                    && type.BaseType == typeof(object)
                    && type.Name.StartsWith("<>", StringComparison.Ordinal)
                    && type.IsDefined(typeof(CompilerGeneratedAttribute), true));
        }

        /// <summary>
        /// Determines if the specified type is a dynamic type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>True if the type is an anonymous type, otherwise false.</returns>
        public static bool IsDynamicType(Type type) {
            if (type == null)
                throw new ArgumentNullException("type");

            return (DynamicType.IsAssignableFrom(type)
                    || ExpandoType.IsAssignableFrom(type)
                    || IsAnonymousType(type));
        }

        /// <summary>
        /// Determines if the specified type is a compiler generated iterator type.
        /// </summary>
        /// <param name="type">The type to check.</param>
        /// <returns>True if the type is an iterator type, otherwise false.</returns>
        public static bool IsIteratorType(Type type) {
            if (type == null)
                throw new ArgumentNullException("type");

            return type.IsNestedPrivate
                && type.Name.StartsWith("<", StringComparison.Ordinal)
                && (EnumerableType.IsAssignableFrom(type) || EnumeratorType.IsAssignableFrom(type));
        }

        /// <summary>
        /// 获取指定类型的名字。
        /// </summary>
        /// <param name="type">类型对象。</param>
        /// <returns>返回类型名字。</returns>
        public static string GetTypeName(Type type) {
            if (IsIteratorType(type))
                type = GetFirstGenericInterface(type);

            if (!type.IsGenericType)
                return type.FullName;

            return type.Namespace
                  + "."
                  + type.Name.Substring(0, type.Name.IndexOf('`'))
                  + "<"
                  + string.Join(", ", type.GetGenericArguments().Select(GetTypeName))
                  + ">";
        }

        /// <summary>
        /// Gets the first generic interface of the specified type if one exists.
        /// </summary>
        /// <param name="type">The target type.</param>
        /// <returns>The first generic interface if one exists, otherwise the first interface or the target type itself if there are no interfaces.</returns>
        static Type GetFirstGenericInterface(Type type) {
            Type firstInterface = null;
            foreach (var @interface in type.GetInterfaces()) {
                if (firstInterface == null)
                    firstInterface = @interface;

                if (@interface.IsGenericType)
                    return @interface;
            }
            return @firstInterface ?? type;
        }

    }

    #endregion

    #region 异常

    /// <summary>
    /// 表示模板编译错误。
    /// </summary>
    [Serializable]
    public class RazorCompileException : Exception {

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        public RazorCompileException() {

        }

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        public RazorCompileException(string message)
            : base(message) {

        }

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        public RazorCompileException(string message, Exception inner)
            : base(message, inner) {

        }

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        protected RazorCompileException(SerializationInfo info, StreamingContext context)
            : base(info, context) {

        }
    }

    /// <summary>
    /// 表示模板解析错误。
    /// </summary>
    [Serializable]
    public class RazorParseException : RazorCompileException {

        /// <summary>
        /// 获取当前发生的所有错误信息。
        /// </summary>
        public IList<RazorError> errors;

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        public RazorParseException(IList<RazorError> errors)
            : base(errors[0].Message) {
            this.errors = errors;
        }
    }

    /// <summary>
    /// 表示模板编译错误。
    /// </summary>
    [Serializable]
    public class RazorComplieException : RazorCompileException {

        /// <summary>
        /// 获取当前发生的所有错误信息。
        /// </summary>
        public CompilerErrorCollection errors;

        /// <summary>
        /// 初始化当前类。
        /// </summary>
        public RazorComplieException(CompilerErrorCollection errors)
            : base(errors[0].ErrorText) {
                this.errors = errors;
        }
    }

    #endregion

}
